from django.utils.deprecation import MiddlewareMixin
import re, redis, json
from rest_framework_jwt.utils import jwt_decode_handler
from jwt.exceptions import ExpiredSignatureError, DecodeError
from django.http import JsonResponse


# RBAC 鉴权中间件
class MyRBAC(MiddlewareMixin):

    # 处理请求
    def process_request(self, request):
        # 1.获取用户访问的地址 请求方法 /admin
        path = request.path_info[1:]
        method = request.method.lower()
        # 2.判断 是否 登录 或者 管理员的请求
        if re.findall(r"login|admin", path, re.I):
            # 继续往后走, 放过该请求
            return None

        # 3. 用户后续的请求，需要权限校验

        # 判断用户是否登录  session / cookie /  jwt token
        payload = self.validate_token(request)

        if payload:
            # 用户已登录， 待权限校验
            redis_conn = redis.Redis(host="localhost", port=6379, password="123", db=3)
            print("解析出的payload数据:", payload)
            # 构造key, 用来取用户的所有权限
            key = "user_permissions_%s"%payload.get("user_id")
            permissions = redis_conn.get(key)
            # 获取该用户的权限列表
            permissions_list = json.loads(permissions.decode())

            # 判端当前请求的地址， 在不在权限列表
            for p in permissions_list:
                # 先比较请求的方法
                if p.startswith(method):
                    # 比较 url地址
                    re_exp = p.split()[1]
                    if re.findall(re_exp, path, re.I):
                        # 具有权限 ， 请求继续向后走
                        return None
            # 无权限访问
            return JsonResponse({"code": 401, "msg": "无权限访问"}, status=200)

        else:
            # token验证失败, 用户没有登录
            return JsonResponse({"code": 403, "msg": "用户未登录"}, status=200)


    @staticmethod
    def validate_token(request):

        # 获取token
        token = request.headers.get("Authorization")
        # 每页token
        if not token:
            return False

        # 有token 则解析
        token = token.split()[1]
        try:
            payload = jwt_decode_handler(token)

            return payload

        except ExpiredSignatureError:
            return False

        except DecodeError:
            return False
